﻿using System;
using System.Xml;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.IO;
using Microsoft.Extensions.Logging;
using MassTransit;
using MassTransit.RabbitMqTransport;
using Athena.Observer;
namespace Athena
{
    public class RabbitMqBusDispatcher
    {
        public const string DefaultVirtualHostName = "dvhn";

        private static Dictionary<string, RabbitMqBusBasicUriString> _dictionaryUriString;

        private RabbitMqBusBasicUriString _rabbitMqBusUriString;
        private IBusControl _busControl;
        private IRabbitMqHost _host;
        private bool _isStart;
        private ILogger _logger;
        private string _exchangeType;

        public RabbitMqBusBasicUriString RabbitMqBusUriString {
            get {
                return _rabbitMqBusUriString;
            }
        }

        private List<Tuple<string, Action<IRabbitMqReceiveEndpointConfigurator>>> _rabbitMqReceiveEndpointConfigList;

        public string ExchangeType {
            get {
                return _exchangeType;
            }
        }

        public IBusControl BusControl {
            get {
                return _busControl;
            }
        }

        public IRabbitMqHost Host {
            get {
                return _host;
            }
        }

        public bool IsStart {
            get {
                return _isStart;
            }
        }

        public RabbitMqBusDispatcher(ILogger logger, string virtualHost, string exchangeType)
        {
            _isStart = false;
            _exchangeType = exchangeType;
            if (_dictionaryUriString == null || _dictionaryUriString.Count <= 0)
            {
                InitializationUri();
            }

            RabbitMqBusBasicUriString tmpBusUriString = null;
            if (_dictionaryUriString.ContainsKey(virtualHost))
            {
                tmpBusUriString = _dictionaryUriString[virtualHost];
                _rabbitMqBusUriString = new RabbitMqBusBasicUriString();
            }
            else if (_dictionaryUriString.ContainsKey(DefaultVirtualHostName))
            {
                tmpBusUriString = _dictionaryUriString[DefaultVirtualHostName];
            }
            else
            {
                throw new Exception($"Not Found {virtualHost} Configure Info");
            }
            _rabbitMqBusUriString = new RabbitMqBusBasicUriString()
            {
                HostUri = tmpBusUriString.HostUri,
                Password = tmpBusUriString.Password,
                UserName = tmpBusUriString.UserName
            };
            _rabbitMqReceiveEndpointConfigList = new List<Tuple<string, Action<IRabbitMqReceiveEndpointConfigurator>>>();
            _logger = logger;
        }

        private void InitializationUri()
        {
            _dictionaryUriString = new Dictionary<string, RabbitMqBusBasicUriString>(StringComparer.OrdinalIgnoreCase);

            var xmlDocument = new XmlDocument();

            var configXmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "rabbitmqhoststring.conf");
            if (!File.Exists(configXmlPath))
            {
                throw new FileNotFoundException("RabbitMQ Host String Xml File Not Found");
            }

            xmlDocument.Load(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "rabbitmqhoststring.conf"));
            if (!xmlDocument.HasChildNodes || xmlDocument.ChildNodes.Count < 2 || !xmlDocument.ChildNodes[1].HasChildNodes)
            {
                throw new Exception("RabbitMQ Host String Xml File Format Error");
            }

            var rabbitMqXml = xmlDocument.ChildNodes[1];

            foreach (XmlNode item in rabbitMqXml.ChildNodes)
            {
                var rabbitMqBusBasicUriString = new RabbitMqBusBasicUriString();
                if (!item.HasChildNodes || item.ChildNodes.Count != 3)
                {
                    throw new Exception("RabbitMQ Host String Xml File Format Error");
                }
                for (int i = 0; i < 3; i++)
                {
                    switch (item.ChildNodes[i].Name.ToLower())
                    {
                        case "username":
                            rabbitMqBusBasicUriString.UserName = item.ChildNodes[i].InnerText;
                            break;
                        case "password":
                            rabbitMqBusBasicUriString.Password = item.ChildNodes[i].InnerText;
                            break;
                        case "hosturi":
                            rabbitMqBusBasicUriString.HostUri = item.ChildNodes[i].InnerText;
                            break;
                        default: throw new Exception("RabbitMQ Host String Xml File Format Error");
                    }
                }
                _dictionaryUriString.Add(item.Name, rabbitMqBusBasicUriString);
            }
        }

        public void Add(Tuple<string, Action<IRabbitMqReceiveEndpointConfigurator>> receiveEndpointConfig)
        {
            if (_busControl == null)
            {
                _rabbitMqReceiveEndpointConfigList.Add(receiveEndpointConfig);
            }
            else
            {
                throw new Exception("Add Action Failed _busControl Is Start Now");
            }
        }

        public async Task Start(IReceiveObserver receiveObserver, Action<IRabbitMqBusFactoryConfigurator> startRabbitMqBusFactoryConfigurator = null)
        {
            _busControl = Bus.Factory.CreateUsingRabbitMq(configure =>
            {
                _host = configure.Host(new Uri(_rabbitMqBusUriString.HostUri), hostConfig =>
                {
                    hostConfig.Username(_rabbitMqBusUriString.UserName);
                    hostConfig.Password(_rabbitMqBusUriString.Password);
                });
                if (startRabbitMqBusFactoryConfigurator != null)
                {
                    startRabbitMqBusFactoryConfigurator.Invoke(configure);
                }
                if (_rabbitMqReceiveEndpointConfigList != null)
                {
                    foreach (var item in _rabbitMqReceiveEndpointConfigList)
                    {
                        configure.ReceiveEndpoint(_host, item.Item1, receiveConfigure =>
                        {
                            receiveConfigure.PrefetchCount = 24;
                            item.Item2(receiveConfigure);
                        });
                    }
                }
                configure.BusObserver(new DefaultBusObserver(_logger));
                if (!string.IsNullOrWhiteSpace(_exchangeType))
                {
                    _logger.LogInformation($"build  {_host} exchange type: {_exchangeType}");
                    configure.ExchangeType = _exchangeType;
                }
            });
            _busControl.ConnectReceiveObserver(receiveObserver);
            await _busControl.StartAsync();
            _isStart = true;
        }
    }
}
